package cn.xx.service.impl;

import cn.xx.dao.ItemDOMapper;
import cn.xx.dao.ItemStockDOMapper;
import cn.xx.dao.StockLogDOMapper;
import cn.xx.dataobject.ItemDO;
import cn.xx.dataobject.ItemStockDO;
import cn.xx.dataobject.StockLogDO;
import cn.xx.error.BusinessException;
import cn.xx.error.EmBusinessError;
import cn.xx.mq.MqProducer;
import cn.xx.service.ItemService;
import cn.xx.service.PromoService;
import cn.xx.service.model.ItemModel;
import cn.xx.service.model.PromoModel;
import cn.xx.validator.ValidationResult;
import cn.xx.validator.ValidatorImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author xiexu
 * @create 2022-04-21 11:01
 */
@Service
public class ItemServiceImpl implements ItemService {

    @Autowired
    private ValidatorImpl validator;

    @Autowired
    private ItemDOMapper itemDOMapper;

    @Autowired
    private ItemStockDOMapper itemStockDOMapper;

    @Autowired
    private PromoService promoService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MqProducer mqProducer;

    @Autowired
    private StockLogDOMapper stockLogDOMapper;

    @Override
    @Transactional
    public ItemModel createItem(ItemModel itemModel) throws BusinessException {
        //校验入参
        ValidationResult result = validator.validate(itemModel);
        if (result.isHasErrors()) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());
        }

        //转化itemmodel->dataobject
        ItemDO itemDO = this.convertItemDOFromItemModel(itemModel);

        //写入数据库
        itemDOMapper.insertSelective(itemDO);
        itemModel.setId(itemDO.getId()); // 将传回来的自增id重新写回itemModel

        ItemStockDO itemStockDO = this.convertItemStockDOFromItemModel(itemModel);
        itemStockDOMapper.insertSelective(itemStockDO);

        //返回创建完成的对象
        return this.getItemById(itemModel.getId());
    }

    private ItemDO convertItemDOFromItemModel(ItemModel itemModel) {
        if (itemModel == null) {
            return null;
        }
        ItemDO itemDO = new ItemDO();
        BeanUtils.copyProperties(itemModel, itemDO);
        return itemDO;
    }

    private ItemStockDO convertItemStockDOFromItemModel(ItemModel itemModel) {
        if (itemModel == null) {
            return null;
        }
        ItemStockDO itemStockDO = new ItemStockDO();
        itemStockDO.setItemId(itemModel.getId());
        itemStockDO.setStock(itemModel.getStock());
        return itemStockDO;
    }

    @Override
    public List<ItemModel> listItem() {
        List<ItemDO> itemDOList = itemDOMapper.listItem();

        //使用Java8的stream API，将ItemDO集合转成ItemModel集合
        List<ItemModel> itemModelList = itemDOList.stream().map(itemDO -> {
            // 通过itemDO的id取到库存itemStockDO
            ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
            // 将itemDO和itemStockDO对象封装成itemModel
            ItemModel itemModel = this.convertModelFromDataObject(itemDO, itemStockDO);
            return itemModel;
        }).collect(Collectors.toList());

        return itemModelList;
    }

    // 根据商品id查询商品信息
    public ItemModel getItemById(Integer id) {
        //根据id获取商品基础信息
        ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);
        if (itemDO == null) {
            return null;
        }
        //根据商品id获取商品库存信息
        ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());

        //将商品基本信息和商品库存信息封装成一个itemModel
        ItemModel itemModel = convertModelFromDataObject(itemDO, itemStockDO);

        //获取活动商品信息(根据商品id查询对应的活动信息)
        PromoModel promoModel = promoService.getPromoByItemId(itemModel.getId());
        //如果秒杀活动存在于该商品的信息中，并且状态是未开始或正在进行中，就把秒杀活动设置在商品上面
        //1表示还未开始，2表示正在进行，3表示已结束
        if (promoModel != null && promoModel.getStatus().intValue() != 3) {
            itemModel.setPromoModel(promoModel);
        }

        return itemModel;
    }

    // 从缓存中获取商品信息
    public ItemModel getItemByIdInCache(Integer id) {
        // 根据商品id去redis缓存中获取商品信息
        ItemModel itemModel = (ItemModel) redisTemplate.opsForValue().get("item_validate_" + id);
        // 如果商品信息为空，就去数据库中查询商品信息，并将商品信息存储中redis缓存中，并设置过期时间10分钟
        if (itemModel == null) {
            itemModel = this.getItemById(id);
            redisTemplate.opsForValue().set("item_validate_" + id, itemModel);
            // 设置redis缓存过期时间为10分钟
            redisTemplate.expire("item_validate_" + id, 10, TimeUnit.MINUTES);
        }
        return itemModel;
    }

    private ItemModel convertModelFromDataObject(ItemDO itemDO, ItemStockDO itemStockDO) {
        ItemModel itemModel = new ItemModel();
        //将商品基本信息拷贝到itemModel里面
        BeanUtils.copyProperties(itemDO, itemModel);
        //再单独将库存信息也设置到itemModel里面
        itemModel.setStock(itemStockDO.getStock());
        //返回itemModel
        return itemModel;
    }

    // redis库存扣减
    @Transactional // 涉及到库存减的操作，所以要保证事务的一致性，加上@Transactional
    public boolean decreaseStock(Integer itemId, Integer amount) throws BusinessException {
        // 将stock库存表中对应的商品id的对应库存数量减去购买数量
        // increment(-1)等于减去1，返回的是计算后的值
        long result = redisTemplate.opsForValue().increment("promo_item_stock_" + itemId, amount.intValue() * -1);
        if (result > 0) {
            // 若redis更新库存成功，直接返回true
            return true;
        } else if (result == 0) { // 商品已售完
            // 打上库存已售罄的标识
            redisTemplate.opsForValue().set("promo_item_stock_invalid_" + itemId, true);
            // 返回更新库存成功
            return true;
        } else {
            // 若redis更新库存失败，需要回滚redis库存，将库存重新补回去
            increaseStock(itemId, amount);
            return false;
        }
    }

    // 异步更新库存，使用rocketmq实现
    public boolean asyncDecreaseStock(Integer itemId, Integer amount) {
        // 更新库存成功，发送消息，减数据库的库存
        boolean mqResult = mqProducer.asyncReduceStock(itemId, amount);
        return mqResult;
    }

    // redis库存回补
    public boolean increaseStock(Integer itemId, Integer amount) throws BusinessException {
        redisTemplate.opsForValue().increment("promo_item_stock_" + itemId, amount.intValue());
        return true;
    }

    @Override
    @Transactional
    public void increaseSales(Integer itemId, Integer amount) throws BusinessException {
        itemDOMapper.increaseSales(itemId, amount);
    }

    // 初始化对应的库存流水
    @Transactional
    public String initStockLog(Integer itemId, Integer amount) {
        StockLogDO stockLogDO = new StockLogDO();
        stockLogDO.setItemId(itemId);
        stockLogDO.setAmount(amount);
        stockLogDO.setStockLogId(UUID.randomUUID().toString().replace("-", ""));
        // 1表示初始状态，2表示下单扣减库存成功，3表示下单回滚
        stockLogDO.setStatus(1);

        stockLogDOMapper.insertSelective(stockLogDO);

        return stockLogDO.getStockLogId();
    }

}
